home *** CD-ROM | disk | FTP | other *** search
/ The Very Best of Atari Inside / The Very Best of Atari Inside 1.iso / mint / mntlb20 / lib / gmon.c < prev    next >
C/C++ Source or Header  |  1992-05-17  |  18KB  |  672 lines

  1. /*
  2.  * monitor(3), mcount() and profil(2) clones for gcc-Tos library
  3.  *
  4.  * Note: these routines need tuning up. they are very space
  5.  * inefficient. the implementation is totally biased towards support
  6.  * for gprof rather than prof (does anyone use prof anymore? why?)
  7.  *
  8.  *    ++jrb    bammi@cadence.com
  9.  */
  10. #include <stddef.h>
  11. #include <memory.h>
  12. #include <unistd.h>
  13. #include <fcntl.h>
  14. #include <assert.h>
  15. #include <osbind.h>
  16. #include <basepage.h>
  17. #include <sysvars.h>
  18. #include <xbra.h>
  19. #ifndef _COMPILER_H
  20. #include <compiler.h>
  21. #endif
  22.  
  23. /* gmon header */
  24. struct gm_header {
  25.     void        *low;        /* low pc  */
  26.     void        *high;        /* hi  pc  */
  27.     unsigned long    nbytes;        /* bytes in header + histo size */
  28. };
  29.  
  30. typedef unsigned short CHUNK; /* type of each histogram entry */
  31.  
  32. struct gm_call {    /* gm call record */
  33.     void    *from;    /* the caller                 */
  34.     void    *to;    /* the called function (callee)        */
  35.     unsigned long ncalls; /* # of calls from FROM to  TO    */
  36. };
  37.  
  38. #define    GMON_FILE    "gmon.out"    /* name of GMON file */
  39.  
  40. /* format of gmon file
  41.  *    gm_header
  42.  *    ((gm_header.nbytes - sizeof(gm_header))/sizeof(CHUNK)) histo entries
  43.  *    gm_call records upto EOF
  44.  */
  45.  
  46.  
  47. /* histogram variables and defines */
  48. #define    HIST_SCALE    2 /* text space scaled into size/HIST_SCALE  CHUNKS */
  49. #define HIST_SHIFT    1 /* HIST_SCALE == 2 ** HIST_SHIFT (assumption) */
  50.               /* 1 <= HIST_SHIFT <= 8          (assumption) */
  51.  
  52. static CHUNK *hist_buffer;    /* histogram buffer */
  53. static unsigned long hist_size; /* size of histogram in bytes */
  54.  
  55. /* call graph variables and defines */
  56. typedef struct  {    /* a to chain element */
  57.     void    *selfpc;    /* the callee's pc */
  58.     unsigned long count;    /* number of times called */
  59.     unsigned short link;    /* link to next in chain (an index) */
  60. } tostruct ;
  61.  
  62. tostruct       *tos;        /* pool of to chain elements */
  63. unsigned short *froms;        /* called from hash chain heads (an index) */
  64.  /* inherent assumption: typeof froms == typeof CHUNK, otherwise
  65.     change code in monstartup() */
  66.  
  67. #define MINARCS    64        /* min # of to's, a rand() # */
  68. #define ARCDENSITY 2        /* scaling of to's (as a % of  textsize) */
  69. #define HASHFRACTION 1        /* scaling of froms over textsize. 
  70.                    note this is very memory wasteful,
  71.                    but the alternatives are worse 
  72.                    beacuse of two reasons:
  73.                    - increase compute requirements(in mcount)
  74.                    - bsr, followed by bsr will loose!
  75.                    the coding of mcount below almost
  76.                    assumes that HASHFRACTION==1
  77.                    anyone else have some brilliant ideas?
  78.                    */
  79. #define HASH_SHIFT 0    /* HASHFRACTION = 2 ** HASH_SHIFT (assumption) */
  80.  
  81. /* housekeeping variables */
  82. static long          profiling;    /* profiling flag */
  83. static unsigned long  textsize;        /* size of profiled text area */
  84. static unsigned short tolimit;        /* max to == 65534, min == MINARCS */
  85. static unsigned short toalloc;        /* next free to record index  */
  86. static void           *s_lowpc;        /* low  pc rounded down to multiples
  87.                        of histo density =
  88.                        (CHUNK size * HIST_SCALE)
  89.                        (assumption: its mult of 2)
  90.                      */
  91.  
  92. #define USL(X)    ((unsigned long)(X))    /* cast X to unsigned long */
  93.  
  94. /* round X down to last multiple of Y */ /* see assumption above */
  95. #define ROUNDDOWN(X,Y)    ( USL(X) & (~(USL((Y)-1))) ) 
  96.  
  97. /* round X up to next multiple of Y */
  98. #define ROUNDUP(X,Y)    ( USL((X)+((Y)-1)) & (~(USL((Y)-1))) )
  99.  
  100. /* functions */
  101. __EXTERN void monstartup __PROTO((void *lowpc, void *highpc));
  102. __EXTERN void monitor __PROTO((void *lowpc, void *highpc, void *buffer,
  103.          unsigned long bufsize,  unsigned int nfunc));
  104. __EXTERN void moncontrol __PROTO((long flag));
  105. __EXTERN void _mcleanup __PROTO((void));
  106. __EXTERN int profil __PROTO((void *buff, unsigned long bufsiz, unsigned long offset,
  107.            int shift));
  108. static void tick __PROTO((void));
  109. static void term __PROTO((void));
  110. static void install_handlers __PROTO((void));
  111. static void remove_handlers __PROTO((void));
  112. static void unlink_handler __PROTO((xbra_struct *me, int exc));
  113. static void build_graph __PROTO((void *caller, void *callee));
  114.  
  115. /*
  116.  * allocate space for histogram and call graph given the sampling
  117.  * range. call monitor to start up profiling.
  118.  */
  119.  
  120. void monstartup(lowpc, highpc)
  121. void *lowpc, *highpc;
  122. {
  123.     unsigned long    monsize; /* size of hist buffer + gm_header rounded */
  124.     void        *buf;    /* hist + gm_header space */
  125.     unsigned long    i;
  126.  
  127.     assert(USL(lowpc) < USL(highpc));
  128.  
  129. #if 0    /* dont define: screws up gmon because of reloc assumptions */
  130.     s_lowpc = lowpc = (void *)
  131.     (ROUNDDOWN(USL(lowpc), sizeof(CHUNK)<<HIST_SHIFT ));
  132. #else
  133.     s_lowpc = lowpc;
  134. #endif
  135.     highpc = (void *)
  136.     (ROUNDUP(USL(highpc), sizeof(CHUNK)<<HIST_SHIFT ));
  137.     textsize = USL(highpc) - USL(lowpc);
  138.  
  139.     /* allocate histogram buffer + gm_header buffer */
  140.     monsize = (textsize >> HIST_SHIFT) * sizeof(CHUNK) +
  141.            sizeof(struct gm_header);
  142.     monsize = ROUNDUP(monsize, sizeof(short));
  143.  
  144.     if((buf = (CHUNK *)malloc(monsize)) == (CHUNK *)0)
  145.     {
  146.     Cconws("monitor: No memory for histogram buffer\r\n");
  147.     froms = (unsigned short *)0;
  148.     tos = (tostruct *)0;
  149.  
  150.     return;
  151.     }
  152.     bzero(buf, monsize);
  153.  
  154.     /* allocate space for graph data structs */
  155.     i = (textsize>>HASH_SHIFT) * sizeof(*froms);
  156.     i = ROUNDUP(i, sizeof(long));
  157.     if((froms = (unsigned short *)malloc(i)) == (unsigned short *)0)
  158.     {
  159.     Cconws("monitor: No memory for FROMs\r\n");
  160.     free(buf);
  161.     tos = (tostruct *)0;
  162.     return;
  163.     }
  164.     bzero(froms, i);
  165.     
  166.     i = textsize * ARCDENSITY / 100;
  167.     if( i < MINARCS)
  168.     i = MINARCS;
  169.     else if ( i > 65534)
  170.     i = 65534;
  171.     tolimit = (unsigned short)i;
  172.     i = ROUNDUP(i*sizeof(tostruct), sizeof(long));
  173.     if((tos = (tostruct *)malloc(i)) == (tostruct *)0)
  174.     {
  175.     Cconws("monitor: No memory for TOs pool\r\n");
  176.     free(froms);
  177.     free(buf);
  178.     froms = (unsigned short *)0;
  179.     return;
  180.     }
  181.     bzero(tos, i);
  182.     toalloc = 0;    /* index of next available element in TOs pool */
  183.  
  184.     monitor(lowpc, highpc, buf, monsize, (unsigned int)tolimit);
  185. }
  186.  
  187.     
  188. /*
  189.  * monitor(3) interface to profil(2)
  190.  * last arg is silly and not used
  191.  */
  192. void monitor(lowpc, highpc, buffer, bufsize, nfunc)
  193. void *lowpc, *highpc, *buffer;
  194. unsigned long bufsize;
  195. unsigned int nfunc;
  196. {
  197.     struct gm_header *hdr;
  198.  
  199.     if(lowpc == 0)
  200.     { /* finished */
  201.         moncontrol(0L);
  202.         _mcleanup();
  203.         return;
  204.     }
  205.  
  206.     s_lowpc = lowpc;    /* in case user is calling */
  207.     /* initialize gm_header */
  208.     hdr = (struct gm_header *)buffer;
  209.     hdr->low = lowpc;
  210.     hdr->high = highpc;
  211.     hdr->nbytes = bufsize;
  212.  
  213.     hist_size = bufsize - sizeof(struct gm_header); /* sizof hist buffer */
  214.     hist_buffer = (CHUNK *)(USL(buffer) + sizeof(struct gm_header));
  215.  
  216.     /* integ. check, (user can call monitor) */
  217.     if((hist_size == 0) ||
  218.            (hist_size <
  219.         (((USL(highpc) - USL(lowpc))>>HIST_SHIFT)*sizeof(CHUNK))) )
  220.     {
  221.         return;
  222.     }
  223.     /* note: difference in scaling semantics from unix */
  224.     moncontrol(1L); /* begin */
  225. }
  226.  
  227. /*
  228.  * control profiling
  229.  */
  230. void moncontrol(flag)
  231. long flag;
  232. {
  233.     if(flag)
  234.     { /* start */
  235.     profil(hist_buffer, hist_size, (unsigned long)s_lowpc, HIST_SHIFT);
  236.     profiling = 0;
  237.     }
  238.     else
  239.     {
  240.     /* stop */
  241.     profil((void *)0, 0L, 0L, 0);
  242.     profiling = 3;
  243.     }
  244. }
  245.  
  246. /*
  247.  * mcount
  248.  *    called as a part of the entry prologue of a profiled function.
  249.  *    the function that calls mcount is the CALLEE, and the function
  250.  *    that called the CALLEE is the CALLER. mcount grabs the
  251.  *    address of the CALLEE and the address of the CALLER off the
  252.  *    stack, and then calls build_graph that incrementally
  253.  *     constructs the call graphs in the FROMs and TOs structures,
  254.  *    keeping track of the number of times the CALLER called CALLEE.
  255.  *    on entry the stack look like:
  256.  *
  257.  *        sp-> |    ret address of CALLEE    |
  258.  *             |--------------------------|
  259.  *             .  CALLEE's locals        .
  260.  *             .__________________________.
  261.  * CALLEEs    fp-> |  CALLERS saved fp    |
  262.  *             |--------------------------|
  263.  *             |  ret address of CALLER    |
  264.  *             |--------------------------|
  265.  *
  266.  * Note: 
  267.  *    -this is true becuase -fomit-frame-pointer and -pg are
  268.  *     incompatible flags (gcc will say so if you try)
  269.  *
  270.  *    -on the 68k, the address of a long count location is passed in a0
  271.  *     we dont use this, it was a convention for the old prof stuff.
  272.  */
  273.  
  274.     __asm__("\
  275.           .text; .even
  276.          .globl mcount    /* note: no `_' */
  277.      mcount:
  278.          movl    sp@,d0        /* CALLEE's address */
  279.          movl    d0,sp@-
  280.          movl    a6@(4),d0    /* CALLERs  address */
  281.          movl    d0,sp@-
  282.          jbsr    _build_graph    /* build_graph(caller, callee) */
  283.          addqw    #8,sp
  284.          rts
  285.          ");
  286.  
  287. /*
  288.  * build_graph
  289.  *    incrementally build the call graph. at each call the CALLER
  290.  *    and CALLEE are specified. this function builds an arc from
  291.  *    CALLER to CALLEE, or increments the arc count if it already
  292.  *    exists.
  293.  *    the graph is maintianed in the structures FROMs and TOs. each
  294.  *    entry in FROMs is the head of a chain of records of all
  295.  *    functions called from FROM. The CALLERs address is hashed
  296.  *    into FROMs
  297.  */
  298. static void build_graph(caller, callee)
  299. void *caller, *callee;
  300. {
  301.     unsigned short    *fromp;      /* hashed ptr into froms         */
  302.     tostruct        *top;      /* current hash chain element        */
  303.     unsigned short    ti;      /* index of current chain element    */
  304.     tostruct        *last;      /* previous element               */
  305.     
  306.     if(profiling)
  307.     return;    /* out if we are not profiling or this is a recursive call */
  308.     profiling++;
  309.     
  310.     /* hash callee, to a pointer into FROMs */
  311.     fromp = (unsigned short *)(USL(caller) - USL(s_lowpc)); /* lowpc orig */
  312.     if(USL(fromp) > textsize)
  313.     {    /* not within profiled text area */
  314.     profiling--;
  315.     return;
  316.     }
  317.     /* scale to an index */
  318.     fromp = (unsigned short *)(USL(fromp) >> (HASH_SHIFT + sizeof(*froms)));
  319.     fromp = &froms[((long)fromp)]; /* hash bucket pointer */
  320.     ti = *fromp;    /* head of the chain */
  321.     if(ti == 0)
  322.     {    /* head is null, first time in the bucket, start a new chain */
  323.     if((ti = ++toalloc) >= tolimit) /* allocate an element from tos pool */
  324.     {    /* ran out */
  325.         profiling = 3; /* give up profiling */
  326.         return;
  327.     }
  328.     *fromp = ti;
  329.     top = &tos[ti];
  330.     top->selfpc = callee;
  331.     top->count  = 1;
  332.     top->link   = 0;
  333.     profiling--;
  334.     return;
  335.     }
  336.     /* otherwise  search the chain */
  337.     for(last = top = &tos[ti]; top->link != 0;  last = top,
  338.                         top = &tos[top->link])
  339.     {
  340.     if(top->selfpc == callee)
  341.     { /* found it */
  342.         top->count++;    /* increment call count */
  343.         if(top == last)
  344.         { /* at the head of the chain already */
  345.         profiling--;
  346.         return;
  347.         }
  348.         /* otherwise bring it to the head */
  349.         ti = last->link;
  350.         last->link = top->link;
  351.         top->link = *fromp;
  352.         *fromp = ti;
  353.         profiling--;
  354.         return;
  355.     }
  356.     }
  357.     /* not found */
  358.     if((ti = ++toalloc) >= tolimit) /* allocate an element from tos pool */
  359.     {    /* ran out */
  360.     profiling = 3; /* give up profiling */
  361.     return;
  362.     }
  363.     /* put it at head of the chain */
  364.     top = &tos[ti];
  365.     top->count = 1;
  366.     top->selfpc = callee;
  367.     top->link = *fromp;
  368.     *fromp = ti;
  369.     
  370.     profiling--;
  371. }
  372.  
  373. /*
  374.  * _mcleanup
  375.  *    dump out the gmon file
  376.  */
  377. void _mcleanup()
  378. {
  379.     int        fd;
  380.     unsigned long   i;
  381.     unsigned short  j;
  382.     unsigned long   frompc;
  383.     struct gm_call  arc;
  384.     
  385.     if((fd = open(GMON_FILE, O_WRONLY|O_CREAT|O_TRUNC, 0644)) < 0)
  386.     {
  387.     Cconws(GMON_FILE); Cconws(": error opening\r\n");
  388.     return;
  389.     }
  390.     
  391.     /* dump the header + histogram */
  392.     if(_write(fd, (void *)(USL(hist_buffer) - sizeof(struct gm_header)),
  393.        hist_size + sizeof(struct gm_header)) != 
  394.        (hist_size + sizeof(struct gm_header)) )
  395.     {
  396.     Cconws(GMON_FILE); Cconws(": error writing\r\n");
  397.     close(fd); return;
  398.     }
  399.  
  400.     /* dump the call graph */
  401.     for( i = 0; i < (textsize >> (HASH_SHIFT + sizeof(*froms))); i++)
  402.     {
  403.     if(froms[i] != 0)
  404.     {
  405.         frompc = USL(s_lowpc) + ( i << (HASH_SHIFT + sizeof(*froms)));
  406.         for(j = froms[i]; j != 0; j = tos[j].link)
  407.         {
  408.         arc.from = (void *)frompc;
  409.         arc.to   = tos[j].selfpc;
  410.         arc.ncalls = tos[j].count;
  411.         if(_write(fd, &arc, sizeof(arc)) != sizeof(arc))
  412.         {
  413.             Cconws(GMON_FILE); Cconws(": error writing\r\n");
  414.             close(fd); return;
  415.         }
  416.         }
  417.     }
  418.     }
  419.     close(fd);
  420. }
  421.  
  422. #ifdef _USE_TIMER_C_
  423. static unsigned short countdown;
  424. #endif
  425. static short installed = 0;    /* reset to 0 before exit */
  426. static unsigned short *bufr;
  427. static unsigned long maxidx;
  428. static unsigned long off;
  429. static unsigned long shift_val;
  430. static void term(void), tick(void);
  431.  
  432. static xbra_struct tick_xbra = _XBRA_INIT(tick);
  433. static xbra_struct term_xbra = _XBRA_INIT(term);
  434.  
  435. extern BASEPAGE *_base;
  436. static BASEPAGE **act_pd;
  437. static BASEPAGE *my_base;
  438.  
  439. /*
  440.  * profil
  441.  *    record pc every N ms into buffer
  442.  *    index into buffer == (pc - offset) >> shift
  443.  *        (note difference in scaling semantics)
  444.  *     turned off by shift == 0
  445.  *    ineffective by bufsiz == 0
  446.  *
  447.  *    on the St, we hook into the Timer C interrupt, and record
  448.  *    every 4'th tick (20 ms).
  449.  *    this method was chosen over user Timer A, so that applications
  450.  *    that use the timer can be profiled.
  451.  *    vbl was not considered because we dont have the flexibility
  452.  *    of changing the time resolution, and because its frequency is
  453.  *    screen rez dependent (plus its harder to get at the user pc!)
  454.  *
  455.  *    xbra protocol to hook in/out handlers. we hook into the terminate
  456.  *    vector independent of the rest of the library to make sure we
  457.  *    unhook before process termination. this is also necessary because
  458.  *    the user can call up these routines independent of gcrt0
  459.  */
  460. int profil(buff, bufsiz, offset, shift)
  461. void *buff;
  462. unsigned long bufsiz, offset;
  463. int shift;
  464. {
  465.     if(shift == 0)
  466.     {
  467.     if(installed)
  468.         remove_handlers();
  469.     installed = 0;
  470.     return 0;
  471.     }
  472.     /* set the params atomically */
  473.     Jdisint(5);
  474. #ifdef _USE_TIMER_C_
  475.     countdown = 4;
  476. #endif
  477.     bufr = (unsigned short *)buff;
  478.     maxidx = bufsiz>>1;    /* max index into short array */
  479.     off = offset;
  480.     shift_val = shift;
  481.  
  482.     if(!installed)
  483.     {
  484.     installed = 1;
  485.     install_handlers();
  486.     }
  487.     Jenabint(5);
  488.     return 0;
  489. }
  490.  
  491. #ifdef _USE_TIMER_C_
  492. /*
  493.  * tick handler
  494.  *    if countdown = 0, record pc
  495.  */
  496. __asm__ ("\
  497.      .text; .even
  498. _tick:
  499.      subqw    #1,_countdown
  500.      jne    1f
  501.  
  502.      movw    #4,_countdown
  503.      moveml    d0-d1/a0,sp@-
  504.      movl    sp@(14),d0    /* get user pc from exception frame */
  505.      subl    _off,d0
  506.     jcs    2f        /* branch if below */
  507.      movl    _shift_val,d1    /* shift it */
  508.      lsrl    d1,d0
  509.      cmpl    _maxidx:l,d0    /* compare with max index */
  510.      jhi    2f        /* branch if out of range */
  511.  
  512.      lsll    #1,d0        /* word index */
  513.     movl    _bufr,a0
  514.      addl    d0,a0        /* incr hist word */
  515.      addqw    #1,a0@
  516. 2:
  517.      moveml    sp@+,d0-d1/a0
  518. 1:
  519.     movl    _tick_xbra+8,sp@-
  520.      rts ");
  521. #else
  522. /*
  523.  * tick handler
  524.  *    in etv_timer timer handoff vector chain (called every 4th tick)
  525.  *    stack at  this point:
  526.  *    <exception frame user pc, sr>            2
  527.  *    <saved d0-d7/a0-a6>                60
  528.  *    <timer calibration .w>                2
  529.  *    <return address to timer C intr routine>    4
  530.  *                            ---
  531.  *                            68 (offset to user pc)
  532.  */
  533. __asm__ ("\
  534.      .text; .even
  535. _tick:
  536.      movl    sp@(68),d0    /* get user pc from exception frame */
  537.      subl    _off,d0
  538.     jcs    1f        /* branch if below */
  539.      movl    _shift_val,d1    /* shift it */
  540.      lsrl    d1,d0
  541.      cmpl    _maxidx:l,d0    /* compare with max index */
  542.      jhi    1f        /* branch if out of range */
  543.  
  544.      lsll    #1,d0        /* word index */
  545.     movl    _bufr,a0
  546.      addl    d0,a0        /* incr hist word */
  547.      addqw    #1,a0@
  548. 1:
  549.     movl    _tick_xbra+8,sp@-    /* call next handler in chain */
  550.      rts ");
  551. #endif
  552.  
  553. /*
  554.  * terminate vector
  555.  */
  556. static void term()    
  557. {
  558.     /* validate  process id */
  559.     if(_base != *act_pd)
  560.     {
  561.     __asm__ volatile("\
  562.              unlk    a6
  563.              jmp    %0@"
  564.              :
  565.              : "a"(term_xbra.next));
  566.     }
  567.     if(installed)
  568.         remove_handlers();
  569.     /* go on to the next guy */
  570.     __asm__ volatile("\
  571.              unlk    a6
  572.              jmp    %0@"
  573.              :
  574.              : "a"(term_xbra.next));
  575. }
  576.  
  577. /*
  578.  * install tick and terminate handlers at the head of the xbra chains
  579.  *    coding thanks to edgar roeder
  580.  */
  581. static void  install_handlers()
  582. {
  583.     long    *sysbase;
  584.  
  585.     sysbase = (long *) get_sysvar((void *) _sysbase);
  586.     switch(sysbase[6])
  587.     {
  588.       case 0x11201985L:
  589.       case 0x02061986L:
  590.       case 0x04241986L:
  591.     act_pd = (BASEPAGE **) 0x602CL;
  592.     break;
  593.       default:
  594.     act_pd = (BASEPAGE **) sysbase[10];
  595.     }
  596.  
  597. #ifdef _USE_TIMER_C_
  598.     tick_xbra.next = (xptr) Setexc(276>>2, _XBRA_VEC(tick_xbra));
  599. #else
  600.     tick_xbra.next = (xptr) Setexc(0x100, _XBRA_VEC(tick_xbra));
  601. #endif
  602.     term_xbra.next = (xptr) Setexc(0x102, _XBRA_VEC(term_xbra));
  603.     my_base = _base;
  604. }
  605.  
  606. /*
  607.  * unlink a handler in a xbra friendly manner from the exc chain
  608.  */
  609. static void unlink_handler(me, exc)
  610. xbra_struct *me;
  611. int exc;
  612. {
  613.     xbra_struct *this, *prev;
  614.     long save_ssp;
  615.     
  616.     this = (xbra_struct *)    /* get head of chain */
  617.     ((unsigned long)Setexc(exc, -1L) - offsetof(xbra_struct, jump));
  618.     if(this == me)
  619.     {    /* at the head, just unlink */
  620.     Setexc(exc, me->next);
  621.     return;
  622.     }
  623.     /* otherwise find me in the chain and unlink */
  624.     save_ssp = Super(0L);
  625.     for(prev = this; this && (this != me); prev = this,
  626.         this = (xbra_struct *)((this->next)
  627.          ? (((char *)(this->next)) - offsetof(xbra_struct, jump)) : 0))
  628.     {
  629.     /* validate the xbra */
  630.     if(this->xbra_magic != _XBRA_MAGIC) 
  631.     {    /* shame on you */
  632.         Super(save_ssp);
  633.         Setexc(exc, me->next); /* nuke it, otherwise it may call ME */
  634.         return;           /* after i am deinstalled */
  635.     }
  636.     }
  637.     
  638.     if(this == me)
  639.     {     /* unlink me from middle of the chain */
  640.     prev->next = this->next;
  641.     Super(save_ssp);
  642.     return;
  643.     }
  644.     /* we are screwed */
  645.     Super(save_ssp);
  646.     Cconws("\r\nwhat the fuck!\r\n\n");
  647. }
  648.  
  649.  
  650. static void remove_handlers()
  651. {
  652.     /* first validate pid */
  653.     if(_base == *act_pd)
  654.     {
  655.     if(_base != my_base)    /* in vfork()ed parallel addr space */
  656.         _base -= 2;
  657.     else
  658.     {
  659.         /* do i need to Super and raise IPL here ?? */
  660.  
  661. #ifdef _USE_TIMER_C_
  662.         unlink_handler(&tick_xbra, 276>>2);
  663. #else
  664.         unlink_handler(&tick_xbra, 0x100);
  665. #endif
  666.         unlink_handler(&term_xbra, 0x102);
  667.         installed = 0;
  668.     }
  669.     }
  670. }
  671.  
  672.